Skip to content

ROX-33198: Instrument inode tracking on file open lsm hook#391

Draft
JoukoVirtanen wants to merge 3 commits intomainfrom
jv-ROX-33198-instrument-inode-tracking-on-file_open-lsm-hook
Draft

ROX-33198: Instrument inode tracking on file open lsm hook#391
JoukoVirtanen wants to merge 3 commits intomainfrom
jv-ROX-33198-instrument-inode-tracking-on-file_open-lsm-hook

Conversation

@JoukoVirtanen
Copy link
Contributor

Description

A detailed explanation of the changes in your PR.

Feel free to remove this section if it is overkill for your PR, and the title of your PR is sufficiently descriptive.

Checklist

  • Investigated and inspected CI test results
  • Updated documentation accordingly

Automated testing

  • Added unit tests
  • Added integration tests
  • Added regression tests

If any of these don't apply, please comment below.

Testing Performed

TODO(replace-me)
Use this space to explain how you tested your PR, or, if you didn't test it, why you did not do so. (Valid reasons include "CI is sufficient" or "No testable changes")
In addition to reviewing your code, reviewers must also review your testing instructions, and make sure they are sufficient.

For more details, ref the Confluence page about this section.

@JoukoVirtanen JoukoVirtanen requested a review from Molter73 March 15, 2026 18:14
// to avoid verifier issues with untrusted pointers.
// We need to replicate the logic from inode_to_key() to handle
// special filesystems like btrfs correctly.
inode_key_t parent_key = {0};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to get the key for the parent using inode_to_key, but got a verifier error. That function is largely copied here. It is not completely copied, because doing so resulted in a verifier error. This will not work in all cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the verifier error and or the code you were using that got the error? We really shouldn't duplicate code and I'm sure we can figure out a way to make the verifier happy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am now using inode_to_key here without getting a verifier error. I modified inode_to_key so that it is able take in an untrusted pointer.

return Ok(());
}

let host_path = host_info::prepend_host_mount(event.get_filename());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial plan was to look up the parent, get its file path, and then add the file name. However, it seems possible to get the entire path from the event.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works if the file is not mounted to a different directory. Try monitoring /etc/monitored on your host, then run something like docker run --rm -it -v /etc/monitored:/in-container fedora:43 and access some files in /in-container. You should see the events have a filename in /in-container with a host path of /etc/monitored.

# Wait for creation event
process = Process.from_proc()
creation_event = Event(process=process, event_type=EventType.CREATION,
file=fut, host_path='')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should host_path be populated here.

"""
cwd = os.getcwd()
config = {
'paths': [f'{monitored_dir}/**', '/mounted/**', '/container-dir/**'],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what globbing to use here.

// to avoid verifier issues with untrusted pointers.
// We need to replicate the logic from inode_to_key() to handle
// special filesystems like btrfs correctly.
inode_key_t parent_key = {0};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the verifier error and or the code you were using that got the error? We really shouldn't duplicate code and I'm sure we can figure out a way to make the verifier happy.

Comment on lines +129 to 130
if path.is_file() || path.is_dir() {
self.metrics.scan_inc(ScanLabels::FileScanned);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to have separate metrics here, so we can check how many files and directories we are tracking exactly.

return Ok(());
}

let host_path = host_info::prepend_host_mount(event.get_filename());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works if the file is not mounted to a different directory. Try monitoring /etc/monitored on your host, then run something like docker run --rm -it -v /etc/monitored:/in-container fedora:43 and access some files in /in-container. You should see the events have a filename in /in-container with a host path of /etc/monitored.

.with_context(|| format!("Failed to add creation event entry for {}", host_path.display()))?;
} else {
debug!("Creation event for non-existent file: {}", host_path.display());
self.metrics.scan_inc(ScanLabels::FileRemoved);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth it to add a separate label for this case, missing a host file is a separate condition IMO.

Comment on lines +242 to +243
if event.is_creation() {
if let Err(e) = self.handle_creation_event(&event) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change edition in fact/Cargo.toml to 2024 we can rewrite this like:

if event.is_creation() && 
    let Err(e) = self.handle_creation_event(&event) {


/// Handle file creation events by adding new inodes to the map.
fn handle_creation_event(&self, event: &Event) -> anyhow::Result<()> {
if self.get_host_path(Some(event.get_inode())).is_some() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition can never be true, the events coming from the kernel don't have the host path, I would remove the block.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a separate test for this? I assume you should be able to tweak the tests in test_file_open.py to test your changes.

}

unsigned long magic = inode->i_sb->s_magic;
unsigned long magic = BPF_CORE_READ(inode, i_sb, s_magic);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inode_to_key needs to be able to use untrusted pointers, so that it can get the key for a parent inode. To get it to be able to use untrusted pointers, BPF_CORE_READ is used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants